package morenoapp.model.impl;

import java.awt.geom.Point2D;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import morenoapp.model.data.MorenoQuestionFace;
import morenoapp.model.data.MorenoRespondentFace;
import morenoapp.model.enums.MorenoSex;
import morenoapp.model.meta.JSON;
import morenoapp.model.view2.MorenoView2Data;
import morenoapp.util.I18N;
import morenoapp.util.MorenoCollections;
import morenoapp.util.ShouldNotHappenException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 *
 * @author dmitry.mamonov
 */
public class MorenoView2Impl implements MorenoView2Data {
    @JSON("Unnamed")
    protected String viewName = I18N.get("sociogram.default_view_name");
    protected Map<Long, Point2D> respondentPosition = new HashMap<Long, Point2D>();
    @JSON("80")
    protected int ringStep = 80;
    @JSON("true")
    protected boolean displayCorrections = true;
    @JSON("false")
    protected boolean displayMetrics = false;
    @JSON("false")
    protected boolean displayNames = false;
    protected Map<Long, Boolean> displayRelationships = new HashMap<Long, Boolean>();
    @JSON("true")
    protected boolean displaySex = true;
    @JSON("false")
    protected boolean displayTooltips = false;
    @JSON("false")
    protected boolean divedeBySex = false;
    @JSON("false")
    protected boolean hideFemale = false;
    @JSON("false")
    protected boolean hideMale = false;
    protected Map<Long, Boolean> hideRespondent = new HashMap<Long, Boolean>();
    @JSON("6")
    protected int displayFirstNRelationships = 6;

    @Override
    public boolean isDisplayCorrections() {
        return displayCorrections;
    }

    @Override
    public void setDisplayCorrections(boolean displayCorrections) {
        this.displayCorrections = displayCorrections;
    }

    @Override
    public boolean isDisplayMetrics() {
        return displayMetrics;
    }

    @Override
    public void setDisplayMetrics(boolean displayMetrics) {
        this.displayMetrics = displayMetrics;
    }

    @Override
    public boolean isDisplayNames() {
        return displayNames;
    }

    @Override
    public void setDisplayNames(boolean displayNames) {
        this.displayNames = displayNames;
    }

    @Override
    public boolean isDisplaySex() {
        return displaySex;
    }

    @Override
    public void setDisplaySex(boolean displaySex) {
        this.displaySex = displaySex;
    }

    @Override
    public boolean isDisplayTooltips() {
        return displayTooltips;
    }

    @Override
    public void setDisplayTooltips(boolean displayTooltips) {
        this.displayTooltips = displayTooltips;
    }

    @Override
    public boolean isDivideBySex() {
        return divedeBySex;
    }

    @Override
    public void setDivideBySex(boolean divedeBySex) {
        this.divedeBySex = divedeBySex;
    }

    @Override
    public boolean isHideFemale() {
        return hideFemale;
    }

    @Override
    public void setHideFemale(boolean hideFemale) {
        this.hideFemale = hideFemale;
    }

    @Override
    public boolean isHideMale() {
        return hideMale;
    }

    @Override
    public void setHideMale(boolean hideMale) {
        this.hideMale = hideMale;
    }

    @Override
    public int getRingStep() {
        return ringStep;
    }

    @Override
    public void setRingStep(int ringStep) {
        this.ringStep = ringStep;
    }

    @Override
    public boolean isDisplayRelationships(MorenoQuestionFace _quest) {
        Boolean result = displayRelationships.get(_quest.getId());
        return result != null ? result : true;
    }

    @Override
    public void setDisplayRelationships(MorenoQuestionFace _quest, boolean _doDisplay) {
        displayRelationships.put(_quest.getId(), _doDisplay);
    }

    @Override
    public Point2D getRespondentPosition(MorenoRespondentFace _resp) {
        Point2D result = respondentPosition.get(_resp.getId());
        return result != null ? result : new Point2D.Double(0.0, 0.0);
    }

    @Override
    public void setRespondentPosition(MorenoRespondentFace _resp, Point2D _p) {
        respondentPosition.put(_resp.getId(), _p);
    }

    @Override
    public boolean isHideRespondent(MorenoRespondentFace _resp) {
        Boolean result = hideRespondent.get(_resp.getId());
        return result != null ? result : false;
    }

    @Override
    public void setHideRespondent(MorenoRespondentFace _resp, boolean _flag) {
        hideRespondent.put(_resp.getId(), _flag);
    }

    @Override
    public boolean isHideRespondentOrSex(MorenoRespondentFace _resp) {
        MorenoSex respSex = _resp.getSex();
        if (respSex == MorenoSex.MALE) {
            if (isHideMale() == true) {
                return true;
            }
        }
        if (respSex == MorenoSex.FEMALE) {
            if (isHideFemale() == true) {
                return true;
            }
        }
        return isHideRespondent(_resp);
    }

    @Override
    public String getViewName() {
        return viewName;
    }

    @Override
    public void setViewName(String _name) {
        this.viewName = _name;
    }
    @Override
    public int getDisplayFirstNRelationships() {
        return displayFirstNRelationships;
    }

    @Override
    public void setDisplayFirstNRelationships(int _value) {
        this.displayFirstNRelationships = _value;
    }

    @Override
    public void applyJSON(JSONObject json) throws JSONException {
        for (Field field : getClass().getDeclaredFields()) {
            JSON jsonMeta = field.getAnnotation(JSON.class);
            if (jsonMeta != null) {
                boolean accessible = field.isAccessible();
                field.setAccessible(true);
                try {
                    if (json.has(field.getName())) {
                        Class<?> type = field.getType();
                        Object value = null;
                        if (type == String.class) {
                            value = json.getString(field.getName());
                        } else if (type == boolean.class) {
                            value = json.getBoolean(field.getName());
                        } else if (type == int.class) {
                            value = json.getInt(field.getName());
                        }

                        if (value == null) {
                            if (type == String.class) {
                                value = jsonMeta.value();
                            } else if (type == boolean.class) {
                                value = Boolean.valueOf(jsonMeta.value());
                            } else if (type == int.class) {
                                value = Integer.valueOf(jsonMeta.value());
                            }
                        }

                        if (value == null) {
                            throw new ShouldNotHappenException("Value is null for field: " + field.getName());
                        }
                        field.set(this, value);
                    }
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger(MorenoView2Impl.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger(MorenoView2Impl.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    field.setAccessible(accessible);
                }
            }
        }

        //custom json
        { //respondetns positions
            JSONObject jsonMap = json.getJSONObject("respondentPosition");
            respondentPosition.clear();
            for(String key:MorenoCollections.toIterable((Iterator<String>)jsonMap.keys())){
                JSONArray arrValue = jsonMap.getJSONArray(key);
                respondentPosition.put(Long.valueOf(key),
                        new Point2D.Double(arrValue.getDouble(0), arrValue.getDouble(1)));
            }
        }
        { //questions
            JSONObject jsonMap = json.getJSONObject("displayRelationships");
            displayRelationships.clear();
            for(String key:MorenoCollections.toIterable((Iterator<String>)jsonMap.keys())){
                displayRelationships.put(Long.valueOf(key), jsonMap.getBoolean(key));
            }
        }
        { //hide respondents
            JSONObject jsonMap = json.getJSONObject("hideRespondent");
            hideRespondent.clear();
            for(String key:MorenoCollections.toIterable((Iterator<String>)jsonMap.keys())){
                hideRespondent.put(Long.valueOf(key), jsonMap.getBoolean(key));
            }
        }
    }

    @Override
    public JSONObject toJSON() throws JSONException {
        JSONObject result = new JSONObject();
        for (Field field : getClass().getDeclaredFields()) {
            JSON jsonMeta = field.getAnnotation(JSON.class);
            if (jsonMeta != null) {
                boolean accessible = field.isAccessible();
                field.setAccessible(true);
                try {
                    result.put(field.getName(), field.get(this));
                } catch (IllegalArgumentException ex) {
                    Logger.getLogger(MorenoView2Impl.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IllegalAccessException ex) {
                    Logger.getLogger(MorenoView2Impl.class.getName()).log(Level.SEVERE, null, ex);
                } finally {
                    field.setAccessible(accessible);
                }
            }
        }

        //custom json
        { //respondetns positions
            JSONObject json = new JSONObject();
            for (Map.Entry<Long, Point2D> entry : respondentPosition.entrySet()) {
                JSONArray arrValue = new JSONArray();
                arrValue.put(entry.getValue().getX());
                arrValue.put(entry.getValue().getY());
                json.put("" + entry.getKey(), arrValue);
            }
            result.put("respondentPosition", json);
        }
        { //questions
            JSONObject json = new JSONObject();
            for (Map.Entry<Long, Boolean> entry : displayRelationships.entrySet()) {
                json.put("" + entry.getKey(), entry.getValue());
            }
            result.put("displayRelationships", json);
        }
        { //hide respondents
            JSONObject json = new JSONObject();
            for (Map.Entry<Long, Boolean> entry : hideRespondent.entrySet()) {
                json.put("" + entry.getKey(), entry.getValue());
            }
            result.put("hideRespondent", json);
        }

        return result;
    }

}
